// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cstddef>
#include <functional>
#include <tuple>

/// `std::hash` specialization to allow storing tuples as keys
/// in `std::unordered_map`.
///
/// Essentially hashes all tuple elements and jumbles the
/// individual hashes together.
namespace std {
template <typename... Ts>
struct hash<std::tuple<Ts...>> {
  using argument_type = std::tuple<Ts...>;
  using result_type = std::size_t;

  result_type operator()(const argument_type& argument) const {
    return hash_tuple(argument, std::make_index_sequence<sizeof...(Ts)>());
  }

 private:
  template <std::size_t I, std::size_t... Is>
  result_type
  hash_tuple(const argument_type& tuple, std::index_sequence<I, Is...>) const {
    auto value = std::get<I>(tuple);
    auto current = std::hash<decltype(value)>{}(value);
    auto seed = hash_tuple(tuple, std::index_sequence<Is...>());

    // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html
    return current + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  }

  result_type hash_tuple(const argument_type&, std::index_sequence<>) const {
    return 0;
  }
};
}  // namespace std
